using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Collections;
using System.Reflection;
using System.Security.Permissions;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Data;
using System.Data.Odbc;
using NumericalRecipes;
using FftLib;
using BmgNet;
using StaticUtility;

namespace ObjectLib
{
	/// <summary>
	/// Bridges two vertices of the same ParsedObject to indicate a common edge.
	/// </summary>
	[Serializable]
	public class OcclusionBridge : IXmlSerializable
	{
		/// <summary>
		/// The paths involved in the occlusion bridge.
		/// </summary>
		public PolygonF Pf1, Pf2;
		/// <summary>
		/// The vertices involved in the occlusion bridge.
		/// </summary>
		public VertexF Vf1, Vf2;
		/// <summary>
		/// Constructs a fully specified occlusion bridge.
		/// </summary>
		/// <param name="Pf1"></param>
		/// <param name="Vf1"></param>
		/// <param name="Pf2"></param>
		/// <param name="Vf2"></param>
		public OcclusionBridge(PolygonF Pf1, VertexF Vf1, PolygonF Pf2, VertexF Vf2)
		{
			this.Pf1 = Pf1;
			this.Pf2 = Pf2;
			this.Vf1 = Vf1;
			this.Vf2 = Vf2;
		}
		/// <summary>
		/// A function that generates two polygons based on the parameters
		//	of this occlusion bridge.  Null output if not possible.  Vertices are
		///	NOT claimed by this function [PathF.ReclaimVertices() is NOT called].
		///	The bridge is NOT removed from Pf1.Owner.Bridges.  Pf1 is NOT disowned.
		/// </summary>
		/// <param name="newOwner1"></param>
		/// <param name="newOwner2"></param>
		public void HackOwner(out PolygonF newOwner1, out PolygonF newOwner2)
		{
			newOwner1=null;
			newOwner2=null;
			//	The existing owner cannot be null
			if(this.Pf1==null)
				return;
			//	This bridge must be intra-polygon.
			if(this.Pf1!=this.Pf2)
				return;
			//	Get indices
			int ind1 = Pf1.IndexOf(Vf1);
			int ind2 = Pf1.IndexOf(Vf2);
			int i=ind1;
			//	Create the first polygon, go from ind1 to ind2 (inclusive).
			newOwner1 = new PolygonF(Pf1.Owner);
			newOwner1.StandardizesAutomatically=false;
			//newOwner1.Add(Vf1);
            Vf1.Polygons.Add(newOwner1);
			while(i!=ind2)
			{
				i++;
				if(i>=Pf1.Count)
					i=0;
                //vf = Pf1[i];
                //vf.Polygons.Remove(Pf1);
                //vf.Polygons.Add(newOwner1);
				//newOwner1.Add(vf);
                newOwner1.Add(Pf1[i]);
			}
			newOwner1.StandardizeOrder();
			newOwner1.Pix = new PixelTagger(newOwner1);
			newOwner1.StandardizesAutomatically=true;
			//	Create the second polygon, go from ind2 to ind1 (inclusive).
			i=ind2;
			newOwner2 = new PolygonF(Pf1.Owner);
			newOwner2.StandardizesAutomatically=false;
			newOwner2.Add(Vf2);
            //Vf2.Polygons.Add(newOwner2);
			while(i!=ind1)
			{
				i++;
				if(i>=Pf1.Count)
					i=0;
                //vf = Pf1[i];
                //vf.Polygons.Remove(Pf1);
                //vf.Polygons.Add(newOwner2);
				//newOwner2.Add(vf);
                newOwner2.Add(Pf1[i]);
			}
			newOwner2.StandardizeOrder();
			newOwner2.Pix = new PixelTagger(newOwner2);
			newOwner2.StandardizesAutomatically=true;
		}
		[NonSerialized]
		private static float dx, dy;
		/// <summary>
		/// Gives the length of the occlusion bridge.  Evaluates to 0.0 if either Vf1 or Vf2 are null.
		/// </summary>
		public double BridgeLength
		{
			get
			{
				if(Vf1==null || Vf2==null)
					return 0.0;
				dx = Vf2.X-Vf1.X;
				dy = Vf2.Y-Vf1.Y;
				return Math.Sqrt(dx*dx+dy*dy);
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="dpix"></param>
		/// <param name="goesForward"></param>
		/// <param name="carryOver">On input, the number of pixels leading the first edge point.  On output,
		/// the number of pixels trailing the last edge point.</param>
		/// <returns></returns>
		public PointF[] EdgePoints(float dpix, bool goesForward, ref float carryOver)
		{
			if(carryOver<0.0f)
				carryOver = 0.0f;
			dx = Vf2.X-Vf1.X;
			dy = Vf2.Y-Vf1.Y;
			PointF pf = new PointF(Vf1.X, Vf1.Y);
			if(!goesForward)
			{
				dx = Vf1.X-Vf2.X;
				dy = Vf1.Y-Vf2.Y;
				pf = new PointF(Vf2.X, Vf2.Y);
			}
			float len = (float)Math.Sqrt(dx*dx+dy*dy)/dpix;
			dx/=len;
			dy/=len;
			pf.X+= dx*carryOver;
			pf.Y+= dy*carryOver;
			len-=carryOver;
			carryOver=0.0f;
			if(len<0)
			{
				carryOver = -len;
				return new PointF[0];
			}

			PointF[] output = new PointF[(int)len+1];
			output[0] = pf;
			carryOver = len % dpix;
			float fi;
			for(int i=1; i<output.Length; i++)
			{
				fi = (float)i;
				output[i] = new PointF(pf.X+dx*fi, pf.Y+dy*fi);
			}
			return output;
		}

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            int depth = reader.Depth;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="")
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "Polygon1":
                            {
                                this.Pf1 = new PolygonF();
                                this.Pf1.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        case "Polygon2":
                            {
                                this.Pf2 = new PolygonF();
                                this.Pf2.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        case "Vertex1":
                            {
                                this.Vf1 = new VertexF();
                                this.Vf1.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        case "Vertex2":
                            {
                                this.Vf2 = new VertexF();
                                this.Vf2.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for an OcclusionBridge object.  Field name = " + reader.Name);
                    }
                }
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("OcclusionBridge");
            writer.WriteElementString("Polygon1", Pf1.SerializationPointer.ToString());
            writer.WriteElementString("Vertex1",Vf1.SerializationPointer.ToString());
            writer.WriteElementString("Polygon2", Pf2.SerializationPointer.ToString());
            writer.WriteElementString("Vertex2",Vf2.SerializationPointer.ToString());
            writer.WriteEndElement();
        }
    }
	[Serializable]
	public class Edge : IComparable, IXmlSerializable
	{
		[NonSerialized]
		public EdgeGroup Group;
		[NonSerialized]
		public PolygonF Owner;
		/// <summary>
		/// Finds the twin owner of this Edge and returns it.  The twin owner is another polygon which potentially owns
        /// this edge besides this.Owner.  The search is conducted by searching through the polygon owners of the member
        /// variables 'Begin' and 'End', the 'Group' member variable is not referenced for this operation.
		/// </summary>
		/// <param name="output">The results are added to the list 'output'.</param>
		public PolygonF TwinOwner()
		{
			if(!IsFullEnclosure && Begin.Polygons.Count>1 && End.Polygons.Count>1)
			{
				int i,j;
				for( i=0; i<Begin.Polygons.Count; i++ )
					if(Begin.Polygons[i]!=this.Owner)
					{
						for( j=0; j<End.Polygons.Count; j++)
						{
							if(End.Polygons[j]!=this.Owner)
							{
								if(Begin.Polygons[i]==End.Polygons[j])
								{
									return Begin.Polygons[i];
								}
							}
						}
					}
			}
            return null;
		}
		/// <summary>
		/// The beginning of the full edge.  Includes the first valid point of the edge.
		/// </summary>
		public VertexF Begin;
		/// <summary>
		/// The end of the full edge.  This is the first valid point of the next edge.
		/// </summary>
		public VertexF End;
		/// <summary>
		/// When this Edge fully encapsulates the polygon, this variable is true.  Otherwise false.
		/// When IsFullEnclosure is true, Begin must be null and End must be null.
		/// </summary>
		public bool IsFullEnclosure;
		/// <summary>
		/// Returns true if the PolygonF Owner contains this in its list of OwnedEdges.
		/// </summary>
		public bool ClaimedByOwner
		{
			get
			{
				if( Owner==null )
					return false;
				return Owner.OwnedEdges.IndexOf(this)>=0;
			}
		}
		/// <summary>
		/// Gets the length of this edge in pixels.
		/// </summary>
		public float EdgeLength
		{
			get
			{
				if(Owner==null)
					return 0.0f;

				if(this.IsFullEnclosure)
					return this.Owner.Circumfrence;

				float output = 0.0f;
				int I = Owner.IndexOf(Begin);
				int J = Owner.IndexOf(End);
				int i=I-1, j;
				double dx, dy;

				if(I==-1 || J==-1)
					return 0.0f;

				while(true)
				{
					//	i denotes the current vertex
					i=i+1;
					if(i==Owner.Count)
						i=0;
					//	j denotes the next vertex
					j=i+1;
					if(j==Owner.Count)
						j=0;

					dx = (double)(Owner[j].X-Owner[i].X);
					dy = (double)(Owner[j].Y-Owner[i].Y);
					output += (float)Math.Sqrt(dx*dx+dy*dy);

					if(J==j)	//	This is the last trip through
						return output;
				}
			}
		}
		/// <summary>
		/// Returns a list of edge points that fall within this Edge, from beginning to end,
		/// where the distance between the edge points is denoted as dpix.
		/// </summary>
		/// <param name="dpix">The distance between edge points in pixels.</param>
        /// <param name="carryOver">The number of pixels to carry over, the path distance between this.Begin and the first point returned.</param>
		/// <returns></returns>
		public PointF[] EdgePoints(float dpix, ref float carryOver)
		{
			if(Owner==null)
				return null;
			if(Owner.Count < 2)
				return null;

			int len = (int)(EdgeLength/dpix)+1;
			if(len<=1)
				return new PointF[0];

			if(carryOver<0.0f)
				carryOver = 0.0f;
			
			//	Allocate the output
			PointF[] output = new PointF[len];
			//	Standardize the order so that moving forward around the border implies that the inside of the polygon is right-perpendicular.
			Owner.StandardizeOrder();

			//	Denotes the amount of weight to be carried over.
			float wCurrent=carryOver, wComing, dx, dy;
			int ct=0,i,j,lastInd;

			//	i denotes the current vertex
			if(this.IsFullEnclosure)
			{
				i=lastInd=0;
			}
			else
			{
				i=Owner.IndexOf(Begin)-1;
				lastInd = Owner.IndexOf(End);
			}
			bool done = false;
			while(!done && ct<output.Length)
			{
				i++;
				if(i==Owner.Count)
					i=0;
				//	j denotes the next vertex
				j=i+1;
				if(j==Owner.Count)
					j=0;
				if(lastInd==j)	//	This is the last trip through
					done=true;
				//	denote the slope for this interval.
				dx = Owner[j].X-Owner[i].X;
				dy = Owner[j].Y-Owner[i].Y;
				//	count the number of steps for this interval.
				wComing = (float)Math.Sqrt((double)(dx*dx+dy*dy))/dpix;
				if(wCurrent+wComing < 1.0f)	//	if there isn't enough to extract an edge point, then move to the next vertex.
				{
					wCurrent+=wComing;
					carryOver = wCurrent;
				}
				else	//	otherwise, within this vertex, get all the edge points.
				{
					//	Add the points to output.
					dx/=wComing;
					dy/=wComing;
					if(	ct<output.Length )
					{
						output[ct++] = new PointF(Owner[i].X+dx*wCurrent, Owner[i].Y+dy*wCurrent);
						while(++wCurrent <= wComing && ct<output.Length)
							output[ct++] = new PointF(Owner[i].X+dx*wCurrent, Owner[i].Y+dy*wCurrent);
					}
					wCurrent-=wComing;	//	carry over the remainder to the next iteration
					carryOver = wCurrent;
				}
			}

			//	This should never happen.  But if it does, re-consolidate the length of the output.
			if( ct < len )
			{
				PointF[] redo = new PointF[ct];
				for(i=0; i<ct; i++)
					redo[i]=output[i];
				return redo;
			}

			return output;
		}
		/// <summary>
		/// Create a full edge that is not a full enclosure.  Assumes the Owner has been standardized.
		/// </summary>
		/// <param name="Owner">The owner (null if not owned).</param>
		/// <param name="Begin">VertexF  The beginning of the Edge.</param>
		/// <param name="End">VertexF  The end of the Edge.</param>
		public Edge(PolygonF Owner, VertexF Begin, VertexF End)
		{
			this.Owner = Owner;
			this.Begin = Begin;
			this.End = End;
			IsFullEnclosure = false;
		}
		/// <summary>
		/// Constructs a Edge that fully encloses the Owner.
		/// </summary>
		/// <param name="Owner"></param>
		public Edge(PolygonF Owner)
		{
			this.Owner = Owner;
			IsFullEnclosure = true;
		}
		/// <summary>
		/// Comparison based on hash codes.
		/// </summary>
		/// <param name="obj">Edge  The comparison object.</param>
		/// <returns>Comparison based on hash codes, -1, 0, or 1.</returns>
		public int CompareTo(object obj)
		{
			//	compare hash
			int thisHash = this.GetHashCode();
			int objHash = obj.GetHashCode();
			if(thisHash < objHash)
				return -1;
			else if(thisHash > objHash)
				return 1;
			return 0;
		}
		/// <summary>
		/// True if this is the same edge as the argument (Owner, Begin, End, IsFullEnclosure), false otherwise.
		/// </summary>
		/// <param name="edge"></param>
		/// <returns></returns>
		public bool SameEdgeAs(Edge edge)
		{
            if(edge==null)
                return false;
			return 
					(edge.Owner==this.Owner)
				&&	(edge.Begin==this.Begin)
				&&	(edge.End==this.End)
				&&	(edge.IsFullEnclosure==this.IsFullEnclosure);
		}
		/// <summary>
		/// True if this is a pairable twin of the provided edge.
		/// </summary>
		/// <param name="edge"></param>
		/// <returns></returns>
		public bool IsTwinOf(Edge edge)
		{
            //  Can't be the same object
            if(this==edge)
                return false;
			//	Full enclosures don't pair with anything.
			if(  this.IsFullEnclosure || edge.IsFullEnclosure )
				return false;
			//	Owners must be different
			if( this.Owner==edge.Owner )
				return false;
            /*
			else
			{
				//	Both are polygons, so the direction is opposite
				if( this.Owner.GetType()==typeof(PolygonF) && edge.Owner.GetType()==typeof(PolygonF) )
					return
							this.End==edge.Begin
						&&	this.Begin==edge.End
						&&	Math.Abs(this.EdgeLength-edge.EdgeLength)<0.001f;	//	== with truncation error
				//	At least one is not a polygon, so direction is unknown (the EdgeLength calculation below resolves this)
				else
				{
					return (
								( this.End==edge.Begin	&& this.Begin==edge.End )
									||
								( this.End==edge.End	&& this.Begin==edge.Begin )
							)
						&&	Math.Abs(this.EdgeLength-edge.EdgeLength)<0.001f; //	== with truncation error
				}
            }
            */
            //	True if the beginning and end vertices must match oppositely and if the length is the same.
            return
                    this.End==edge.Begin
				&&	this.Begin==edge.End
				&&	Math.Abs(this.EdgeLength-edge.EdgeLength)<0.001f;	//	== with truncation error
		}
		/// <summary>
		/// Determins whether two Edge objects can be joined.  They don't have to be owned
		/// by the same Polygon.
		/// </summary>
		/// <param name="edge"></param>
		/// <returns></returns>
		public bool CanJoinWith(Edge edge)
		{
            //  Can't join with itself
            if(this==edge)
                return false;
            //  Full enclosures can't join with anything
			if(this.IsFullEnclosure || edge.IsFullEnclosure)
				return false;
			//	If there is a common Begin or End vertex (order irrelevant) but not both.
			if( this.Begin==edge.Begin )
				if(this.End != edge.End) //	begin are equal, end are (not)
					return true;
			if( this.End==edge.End )	//	begin are not equal, end are equal
				return true;
			if( this.IsTwinOf(edge) )	//	Cannot join its twin
				return false;
			//	Since the edges are not twins, the following statement is only true if the following two edges create a closed shape
            //  when attached end-to-end, probably they are part of the same polygon, but maybe not.
			return this.Begin==edge.End || this.End==edge.Begin;
		}
		/// <summary>
		/// Determines whether an owners vertex at the specified index falls within the scope of this Edge object.
		/// </summary>
		/// <param name="ind">An index into the owner's set of vertices.</param>
        /// <returns>Ttrue if the specified index in the Owner polygon falls within this Edge or on its borders, false otherwise.  False if Owner==null.</returns>
		public bool OwnsIndex(int ind)
		{
			if(Owner==null)
				return false;
            if(ind<0 || ind>=Owner.Count)
                return false;
			if(IsFullEnclosure)
				return true;
			int begInd = Owner.IndexOf(Begin);
			int endInd = Owner.IndexOf(End);
			// no wrap
			if( begInd < endInd && ind>=begInd && ind<=endInd )
				return true;
			//	wrap
			if( begInd > endInd && (ind>=begInd || ind<=endInd) )
				return true;
			return false;
		}
		#region PictureBox Drawing (ObjectParser functionality)
		[NonSerialized]
		private PointF[] pbEdgeIn;
		[NonSerialized]
		private uint pbChangeCt_last=0u;
		[NonSerialized]
		private float pbEdgeIn_offsetDistance_last=Single.NaN;
		[NonSerialized]
		private static int _begInd, _endInd, _nVerts=0, _i, _j, _ownerCt;
		[NonSerialized]
		private static PointF[] _pbPoly;
		/// <summary>
		/// Gets the picture-box coordinates of a polygon.  The output is freshly initialized memory, it does
        /// not point to an internal field of the object.
		/// </summary>
		/// <param name="refPoly">The reference polygon that the edge is drawn with respect to.</param>
		/// <param name="offsetDistance">The distance of offset from the polygon's actual edge (in pixels).  
		/// Positive distance draws the edge inside the polygon, negative distance is outside.</param>
		/// <param name="useCacheIfPossible">True if the cached calculation of this function can be used.  
		/// The cache is used if IFormMain.GetImgRectChangeCt() has not changed since the last calculation, 
		/// if refPoly did not change, and if offsetDistance is the same.  If False is supplied, 
		/// then the function is recalculated.  False should be supplied when the edge is actually changed.</param>
		public PointF[] PbEdgeIn(float offsetDistance, bool useCacheIfPossible)
		{
			//	Use cache if possible
			if( useCacheIfPossible
				&& pbChangeCt_last==Owner.Owner.Category.Owner.Owner.GetImgRectChangeCt()
				&& offsetDistance==pbEdgeIn_offsetDistance_last)
				return pbEdgeIn;

			//	Update caching variables.
			pbChangeCt_last=Owner.Owner.Category.Owner.Owner.GetImgRectChangeCt();
			pbEdgeIn_offsetDistance_last=offsetDistance;

			_ownerCt = Owner.Count;

			if(IsFullEnclosure)
				_nVerts = _ownerCt+1;
			else
			{
				_begInd = Owner.IndexOf(Begin);
				_endInd = Owner.IndexOf(End);
				_nVerts = _endInd-_begInd+1;
				if(_nVerts<=0)
					_nVerts+= _ownerCt;	//	unwrap
			}

			//	This may be a bad edge, return null in that case.
			if( _begInd==-1 || _endInd==-1 )
				return null;

			//	Reallocate memory if necessary
			if(pbEdgeIn == null)
			{
				pbEdgeIn = new PointF[_nVerts];
			}
			else if(pbEdgeIn.Length != _nVerts)
			{
				pbEdgeIn = new PointF[_nVerts];
			}

			_pbPoly = Owner.PbPath;
			if(IsFullEnclosure)	//	extract the entire edge
			{
				for(_i=0; _i<_pbPoly.Length; _i++)
					pbEdgeIn[_i] = _pbPoly[_i];
				//	this wraps around to display a "closed polygon"
				pbEdgeIn[_nVerts-1] = _pbPoly[0];
			}
			else	//	extract the sub-edge
			{
				_j = _begInd;
				for(_i=0; _i<_nVerts; _i++)
				{
					pbEdgeIn[_i] = _pbPoly[_j++];
					if(_j==_ownerCt)
						_j=0;
				}
			}

			//	Move the points to the right (inward) by offsetDistance display (FormParse.Pb) pixels.
			Util.MoveRightwards(pbEdgeIn, offsetDistance);

			return pbEdgeIn;
		}
		#endregion
		public override string ToString()
		{
			if(Begin!=null && End!=null)
			{
				string output = "";
				if(Owner!=null && Owner.Owner!=null)
				{
					output = Owner.Owner.Name + "("
						+ Convert.ToString(Owner.Owner.Polygons.IndexOf(Owner)) + ")";
				}
				return output + Begin.ToString() + End.ToString();
			}
			else if(IsFullEnclosure)
			{
				string output = "";
				if(Owner!=null && Owner.Owner!=null)
				{
					output = Owner.Owner.Name + "("
						+ Convert.ToString(Owner.Owner.Polygons.IndexOf(Owner)) + ")";
				}
				return output + "[Full]";
			}
			else
			{
				return "Invalid Edge";
			}
		}
		/// <summary>
		/// Returns ToString()
		/// </summary>
		public string Name
		{
			get
			{
				return ToString();
			}
		}
		/// <summary>
		/// For storage, ideally for use in associating filter responses with each edge.
		/// </summary>
		[NonSerialized]
		public FilterResponseList CachedFilters;
		/// <summary>
		/// For storage, ideally for use in associating filter responses with each edge.
		/// </summary>
		[NonSerialized]
		public PointF[] CachedEdgePoints;
		/// <summary>
		/// For storage, ideally for use in associating filter responses with each edge.
		/// </summary>
		[NonSerialized]
		public float[] CachedFilterDistances;
		/// <summary>
		/// For storage, ideally for use in associating filter responses with each edge.
		/// </summary>
		[NonSerialized]
		public float[] CachedFilterPathDistances;
		[NonSerialized]
		private static float _l, _r, _t, _b;
		[NonSerialized]
		private VertexF _vf;
		/// <summary>
		/// Bounding rectangle of the edge.
		/// </summary>
		/// <param name="cushion"></param>
		/// <returns></returns>
		public RectangleF BoundingRect(float cushion)
		{
			if(Owner==null || Owner.Count==0)
				return RectangleF.Empty;
			_vf = Owner[0];
			_l=_r=_vf.X;
			_t=_b=_vf.Y;
			for(int i=0; i<Owner.Count; i++)
			{
				_vf = Owner[i];
				if(_vf.X<_l)
					_l=_vf.X;
				else if(_vf.X>_r)
					_r=_vf.X;
				if(_vf.Y<_t)
					_t=_vf.Y;
				else if(_vf.Y>_b)
					_b=_vf.Y;
			}
			cushion /= 2.0f;
			_l-=cushion;
			_r+=cushion;
			_t-=cushion;
			_b+=cushion;
			return new RectangleF(_l, _t, _r-_l, _b-_t);
		}
        /// <summary>
        /// Serializes this object into the StreamWriter sw in XML format.  Each entry has 'ntabs' tabs inserted at the start of each line.
        /// </summary>
        /// <param name="sw"></param>
        /// <param name="ntabs"></param>
        public void SerializeXmlToStream(StreamWriter sw,int ntabs)
        {
            //  Generate the prefix for each line
            //string tabs0 = new string((char)9,ntabs);

            //  Start the XML element
            sw.WriteLine("<Edge>");
            if( this.IsFullEnclosure )
            {
                //  Store Full Enclosure Status
                sw.WriteLine("<IsFullEnclosure>true</IsFullEnclosure>");
            }
            else
            {
                //  Store Full Enclosure Status
                sw.WriteLine("<IsFullEnclosure>false</IsFullEnclosure>");
                //  Store Begin
                sw.WriteLine("<BeginVertex>"+Begin.SerializationPointer.ToString()+"</BeginVertex>");
                //  Store End
                sw.WriteLine("<EndVertex>"+End.SerializationPointer.ToString()+"</EndVertex>");
            }
            //  Finish the XML element
            sw.WriteLine("</Edge>");
        }

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            this.IsFullEnclosure = false;
            int depth = reader.Depth;
            bool reading=false;
            while( (reading || reader.Read()) && reader.Depth>depth)
            {
                reading = false;
                if((reader.NodeType == System.Xml.XmlNodeType.Element || reader.NodeType==System.Xml.XmlNodeType.Text) && reader.Name!="" && !this.IsFullEnclosure)
                {
                    reading = true;
                    switch(reader.Name)
                    {
                        case "IsFullEnclosure":
                            {
                                this.IsFullEnclosure = Boolean.Parse(reader.ReadElementContentAsString());
                            } break;
                        case "BeginVertex":
                            {
                                this.Begin = new VertexF();
                                this.Begin.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        case "EndVertex":
                            {
                                this.End = new VertexF();
                                this.End.SerializationPointer = reader.ReadElementContentAsInt();
                            } break;
                        default: throw new Exception("Unexpected field encounterd while reading XML for an Edge object.  Field name = " + reader.Name);
                    }
                }
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            if(!IsFullEnclosure && Begin!=null && End!=null && Begin.SerializationPointer>=0 && End.SerializationPointer>=0)
            {
                writer.WriteStartElement("Edge");
                writer.WriteElementString("IsFullEnclosure",IsFullEnclosure.ToString());
                if(Begin!=null && End!=null)
                {
                    writer.WriteElementString("BeginVertex",Begin.SerializationPointer.ToString());
                    writer.WriteElementString("EndVertex",End.SerializationPointer.ToString());
                }
                else
                {
                    writer.WriteElementString("BeginVertex","null");
                    writer.WriteElementString("EndVertex","null");
                }
                writer.WriteEndElement();
            }
        }
    }
}